Skip to content

Modernize codebase: housekeeping, DeleteCollection, and scope-hardened GetDocument#125

Merged
hsinatfootprintai merged 2 commits intomainfrom
refactor/modernize-codebase
Apr 24, 2026
Merged

Modernize codebase: housekeeping, DeleteCollection, and scope-hardened GetDocument#125
hsinatfootprintai merged 2 commits intomainfrom
refactor/modernize-codebase

Conversation

@hsinatfootprintai
Copy link
Copy Markdown
Contributor

Summary

Comprehensive modernization pass informed by a full-repo audit. Two commits:

  1. chore: modernize build, CI, and repo hygiene — no API behavior change.
  2. refactor(app): relocate server, split handlers, implement DeleteCollection — structural reorg bundled with the new DeleteCollection behavior and the hardened GetDocument scope check.

What's in the chore commit

  • .gitignore expanded (binary, coverage, IDE files, .env).
  • New Makefile with build, test, test-race, test-full, vet, tidy, run-local, run-postgres, gen-proto, clean, help.
  • CI: Go bumped from 1.21.7 → 1.24.1 (matches go.mod toolchain); explicit go vet and go build steps; -race enabled on tests.
  • Latent bug fixes surfaced by enabling go vet:
    • 7 copylocks violations in pkg/models/collections/schema.go where SwagValueValue was copied by value, carrying a sync.Mutex via structpb.Value. Fixed by switching to pointer receivers and pointer conversions.
    • Malformed GORM struct tag on ModelFieldSchema.FieldName.
    • Two integration tests (TestDocumentCURD_Delete, TestDocumentCURD_Delete_WithWrongScope) missing testing.Short() guards — would have broken CI's -short run.
  • GORM index tags added on frequently queried FK columns (ModelCollection.ModelProjectID, ModelSchema.ModelCollectionID) and a composite docScope index on ModelDocument(model_project_id, model_collection_id, created_at).
  • migrations/ scaffold with baseline SQL mirroring the new indexes for production (where --restcol_auto_migrate=false), plus a README explaining the workflow.
  • README rewritten with correct ports (50090/50091), real curl examples for every endpoint, architecture diagram, repo layout, config flags, and a dev-only-auth warning.
  • Minor: two typos, context.TODO()context.Background() in 13 test spots.

What's in the refactor commit

Structural (no behavior change):

  • integrationtest/server/server.gopkg/server/app/app.go — production server no longer lives under integrationtest/.
  • pkg/dummy/pkg/bootstrap/ with DummyModelProjectDefaultModelProject.
  • pkg/app/app.go (536 lines) split into topical files: app.go (114), collections.go (152), documents.go (317). Every exported method got a doc comment.

API changes:

  • Proto: added bool force = 3 to DeleteCollectionRequest. Regenerated api/pb/* and api/openapiv2/*.swagger.json.
  • DeleteCollection implemented (was NotImpl):
    • Missing collection_idBadParameters
    • Non-existent collection → NotFound
    • Non-empty collection without forceStatusConflicted (message guides caller to pass force=true)
    • force=true → cascade soft-deletes documents first
  • GetDocument scope hardening: storage uses .Find() which returns (emptyRecord, nil) on scope mismatch; handler now converts empty ID into NotFound to prevent blank success responses under the wrong project/collection. Also added input validation for empty/malformed IDs.
  • getProjectIdFromCtx: previously logged and swallowed GetProjectID() errors; now propagates.

Storage additions for DeleteCollection:

  • CollectionCURD.Delete
  • DocumentCURD.CountByCollection
  • DocumentCURD.DeleteByCollection

Tests:

  • pkg/app/handlers_test.go with table-driven coverage of the new behavior and a fixedProjectResolver test double. Guarded by testing.Short() so CI stays postgres-free.

Deliberately deferred

  • golang-migrate swap at server startup. AutoMigrate is already opt-in via --restcol_auto_migrate (defaults to off in production), so the urgency is low; the full swap needs a baseline-matching migration and staged rollout. Migration files + README lay the groundwork.
  • goja JS sandbox hardening (pkg/runtime/js) — product-level decision on timeout/memory policy.
  • Replacing github.com/sdinsure/agent — black-box dep; out of scope.

Test plan

  • go vet ./...
  • go build ./...
  • go test -race ./... -short
  • make run-postgres && make test-full (requires a clean local postgres)
  • Manual sanity check against curl examples in the new README

🤖 Generated with Claude Code

hsinhoyeh and others added 2 commits April 25, 2026 02:11
Housekeeping changes with no API behavior impact. All edits produce the
same wire-level responses and the same database state as before.

Build & CI
- Expand .gitignore to cover the restcol binary, coverage output, IDE
  directories, and env files.
- Add a Makefile with build, test, test-race, test-full, vet, tidy,
  run-local, run-postgres, gen-proto, clean, and help targets.
- Upgrade CI to Go 1.24.1 (matches go.mod toolchain), add explicit
  `go vet` and `go build` steps, and run tests with the race detector.

Latent bugs surfaced by enabling `go vet` in CI
- pkg/models/collections/schema.go: fix 7 copylocks violations by
  switching SwagValueValue methods to pointer receivers and using
  pointer conversions instead of dereferencing structpb.Value (which
  carries a sync.Mutex via protoimpl.MessageState). Also fix a
  malformed GORM struct tag on ModelFieldSchema.FieldName.
- pkg/storage/documents/documents_test.go: add `testing.Short()` skip
  guards to TestDocumentCURD_Delete and TestDocumentCURD_Delete_WithWrongScope
  so CI's `-short` run does not try to connect to a non-existent DB.
- Replace 13x `context.TODO()` with `context.Background()` in document
  storage tests.

GORM indexes
- Add `index` tags on ModelCollection.ModelProjectID,
  ModelSchema.ModelCollectionID, and a composite `docScope` index on
  ModelDocument (model_project_id, model_collection_id, created_at) to
  back ListByProjectID, GetLatestSchema, ModelDocument.Query, and the
  new Count/DeleteByCollection helpers.

Migrations
- Add a `migrations/` directory with a baseline SQL migration that adds
  the same three indexes explicitly for environments running with
  --restcol_auto_migrate=false (i.e. production). Includes a README
  documenting the workflow and the deferred work to fully wire
  golang-migrate.

Docs & typos
- Rewrite README.md with correct ports (50090 gRPC / 50091 HTTP), real
  curl examples for every endpoint, architecture diagram, repo layout,
  configuration flags, and an explicit warning that the shipped
  AnnonymousClaimParser + AllowEveryOne combo is dev-only.
- Fix two typos: "foreigh" -> "foreign", "shema:" -> "schema:".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ction

Two changes intertwined: a pure structural refactor and the behavior
additions from Wave 5 of the modernization effort. Separated in the diff
but shipped together because they share files.

Structural refactor (no behavior change)
- Move production server code from integrationtest/server/server.go to
  pkg/server/app/app.go; package integrationtestserver -> serverapp. The
  old location confused production vs. integration-test boundaries.
- Rename pkg/dummy -> pkg/bootstrap; DummyModelProject -> DefaultModelProject,
  NewDummyProject -> NewDefaultProject. Added a package doc explaining why
  the default project exists (anonymous auth needs a tenant).
- Split pkg/app/app.go (536 lines) into app.go (struct, constructor,
  shared helpers, swagger), collections.go (CRUD for collections), and
  documents.go (CRUD + query for documents). Every exported method now
  carries a doc comment.
- Update main.go, integrationtest/suite.go, integrationtest/setup_test.go
  imports to match.

API changes
- Proto: add `bool force = 3` to DeleteCollectionRequest (api/restcol.proto).
  Regenerated api/pb/* and api/openapiv2/*.swagger.json via `buf generate`.
- Implement DeleteCollection. Previously returned NotImpl. New behavior:
  - Missing collection_id -> BadParameters
  - Non-existent collection -> NotFound
  - Non-empty collection without force -> StatusConflicted
  - Non-empty collection with force=true -> cascade-deletes all documents
    before soft-deleting the collection itself
- Harden GetDocument scope check. Storage.Get uses .Find(), which returns
  an empty record + nil error when the (project, collection, document)
  tuple does not match. The handler now converts an empty ID into a
  NotFound error so a wrong project or collection ID cannot produce a
  blank success response.
- Add input validation to GetDocument: empty collection_id or document_id
  returns BadParameters; invalid document_id format likewise.
- Fix error-swallow bug in getProjectIdFromCtx: when ProjectInfor.GetProjectID
  fails the error was logged and dropped. Now propagates.

Storage additions
- CollectionCURD.Delete(ctx, tableName, pid, cid) for the DeleteCollection
  path.
- DocumentCURD.CountByCollection and DeleteByCollection to support the
  reject-vs-cascade decision and the cascade operation itself.

Tests
- Add pkg/app/handlers_test.go with table-driven handler tests covering:
  DeleteCollection validation, existence, empty-success, non-empty
  reject-without-force (StatusConflicted), force-cascade (document gone
  afterward); GetDocument validation matrix and the new cross-collection
  scope-mismatch -> NotFound assertion; DeleteDocument validation;
  GetCollection missing-id rejection. All tests are guarded by
  testing.Short() so CI's -short path stays postgres-free.
- Add a fixedProjectResolver test double implementing
  sdinsureruntime.ProjectResolver so handlers can be exercised without
  the full gRPC middleware stack.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hsinatfootprintai hsinatfootprintai merged commit 8dc5701 into main Apr 24, 2026
1 check passed
@hsinatfootprintai hsinatfootprintai deleted the refactor/modernize-codebase branch April 24, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants